<?php
/***********************************************************************
 * @package     IvanEngine
 * @subpackage  Framwork
 * @author      Ivan <ivanzx@msn.com>
 * @created     2010-05-30
 ***********************************************************************/
if(defined("IVANENGINE_CORE_IO_XML"))
    die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_CORE_IO_XML", true);

require_once (dirname(__FILE__).'/../Common.php');
require_once (dirname(__FILE__).'/../STL/Tree.php');

class CXmlNode extends CTree
{
    protected
        $m_attribute    = null,
        $m_content      = null;
        
    public function __construct($name) 
    {
        parent::__construct($name);
        $this->m_attribute  = new CMap();
    }
    
    public function __destruct()
    {
        unset($this->m_attribute);
        parent::__destruct();
    }
    
    public function GetContent()
    {
        return $this->m_content;
    }
    
    public function SetContent($content)
    {
        $this->m_content    = $content;
    }
    
    public function & GetAttributes()
    {
        return $this->m_attribute;
    }
    
    public function GetAttribute($name)
    {
        return $this->m_attribute->Get($name);
    }
    
    public function SetAttribute($name, $val)
    {
        $this->m_attribute->Set($name, $val);
    }
    
    public function GetAttributeCount()
    {
        return $this->m_attribute->Size();
    }
    
    public function AddAttribute($name, $val)
    {
        $this->m_attribute->Add($name, $val);
    }
} 


class CXml extends CObject implements Serializable
{
    // name sign type
    const NST_TAG           = 1;
    const NST_ATTR          = 2;
    const NST_TEXT          = 3;
    
    protected   $m_root     = null;
    protected   $m_doc      = null;
    protected   $m_fileName = null;
    protected   $m_nst      = self::NST_TAG; // name sign type
    protected   $m_nas      = null; // name attribute sign
    public function __construct($nst = CXml::NST_TAG, $nas = null) 
    {
        parent::__construct();
        $this->m_nst   = $nst;
        $this->m_nas   = $nas;
        if( $this->m_nst != self::NST_TAG &&
            $this->m_nst != self::NST_ATTR &&
            $this->m_nst != self::NST_TEXT )
        {
            throw new CExceptionInvalidArgument('nst parameter only is '.
                'NST_TAG/NST_ATTR/NST_TEXT');
        }
        if( $this->m_nst == self::NST_ATTR && 
            $this->m_nas == null )
        {
            throw new CExceptionInvalidArgument(
                'When nst the time parameter is NST_ATTR, nas parameter must be set');
        }
    }
    
    public function __destruct()
    {
        unset($this->m_doc);
        unset($this->m_root);
        parent::__destruct();
    }
    
    public function & GetRoot()
    {
        return $this->m_root;
    }
    
    public function Load($fileName)
    {
        $this->m_fileName= $fileName;
        $this->m_doc    = new DOMDocument();
        $this->m_doc->load($fileName);
        $rootNode       = $this->m_doc->firstChild;
        $this->m_root   = new CXmlNode($rootNode->nodeName);
        foreach( $rootNode->attributes as $attribute => $value ){
            $this->m_root->AddAttribute($attribute, $value->value);
        }
        $this->_RecursionNode($rootNode, $this->m_root);
    }
    
    public function Save($fileName = null)
    {
        if( $fileName == null ){
            $this->m_doc->save($this->m_fileName);
        }else{
            $this->m_doc->save($fileName);
        }
    }
    
    public function serialize()
    {
        return serialize(array($this->m_fileName, $this->m_root));
    }
    
    public function unserialize($string)
    {
        $array = unserialize($string);
        $this->m_fileName   = $array[0];
        $this->m_root       = $array[1];
    }
    
    protected function _RecursionNode(DOMNode & $node, CXmlNode & $xmlNode)
    {
        if( $node == null || $xmlNode == null ){
            return;
        }
        
        if( $node->hasChildNodes() )
        {
            foreach( $node->childNodes as $child )
            {
                if( get_class($child) != "DOMElement" && 
                    get_class($child) != "DOMNode")
                {
                    if( get_class($child) != "DOMComment" &&
                        trim($child->nodeValue) != "" )
                    {
                        $xmlNode->SetContent($child->nodeValue);
                    }
                    continue;
                }
                $name   = null;
                if( $this->m_nst == self::NST_TAG ){
                    $name   = $child->nodeName;
                }else if( $this->m_nst == self::NST_TEXT ){
                    $name   = $child->nodeValue;
                }else{
                    $name   = $child->getAttribute($this->m_nas);
                    if( $name == null ){
                        continue;
                    }
                }
                $newXmlNode = new CXmlNode($name);
                foreach( $child->attributes as $attribute => $value ){
                    $newXmlNode->AddAttribute($attribute, $value->value);
                }
                $xmlNode->AddChild($newXmlNode);
                $result = $this->_RecursionNode($child, $newXmlNode);
            }
        }
    }
}

?>
